import {
  Directive,
  OnInit,
  Input,
  ElementRef,
  ViewContainerRef,
  ComponentFactory,
  ComponentFactoryResolver,
  SimpleChanges,
  EventEmitter,
  Output,
  TemplateRef,
  Renderer2,
  OnChanges,
} from '@angular/core';
import { Observable } from 'rxjs';
import { LvSizeLD } from '../core/api';
import { MenuItem } from './interface';
import { DropdownComponent } from './dropdown.component';

@Directive({
  selector: '[lv-dropdown], [lvDropdown]',
  exportAs: 'lvDropdown',
  host: {
    '(click)': '_open($event)',
    '(mouseenter)': '_open($event)',
    '(contextmenu)': '_open($event)',
    '(mouseleave)': '_close($event)',
    '[class.lv-dropdown-disabled]': 'lvDisabled',
  },
})
export class DropdownDirective implements OnInit, OnChanges {
  @Input('lvDropdownMenus') lvMenus: MenuItem[];

  @Input('lvDropdownPanelClass') lvPanelClass: string;

  @Input('lvDropdownSize') lvSize: LvSizeLD = 'default';

  @Input('lvDropdownDisabled') lvDisabled: boolean = false;

  @Input('lvDropdownOuterClosable') lvOuterClosable = true;

  @Input('lvDropdownTheme') lvTheme: 'light' | 'dark' = 'light'; // 待实现

  @Input('lvDropdownItemClickClose') lvItemClickClose: boolean = true;

  @Input('lvDropdownLabelTemplate') lvLabelTemplate: TemplateRef<void>;

  @Output('lvDropdownShow') lvShow: EventEmitter<any> = new EventEmitter();

  @Output('lvDropdownHide') lvHide: EventEmitter<any> = new EventEmitter();

  @Input('lvDropdownTrigger') lvTrigger: 'click' | 'hover' | 'contextmenu' = 'click';

  @Output('lvDropdownItemClick') lvItemClick: EventEmitter<any> = new EventEmitter();

  @Input('lvDropdownBeforeOpen') lvBeforeOpen: () =>
    | (false | void | object)
    | Promise<false | void | object>
    | Observable<boolean | void | object>;

  @Input('lvDropdownPosition') lvPosition:
    | 'topLeft'
    | 'topCenter'
    | 'topRight'
    | 'bottomLeft'
    | 'bottomCenter'
    | 'bottomRight' = 'bottomLeft';

  public dropdown: DropdownComponent;

  private ComponentFactory: ComponentFactory<DropdownComponent> =
    this.resolver.resolveComponentFactory(DropdownComponent);

  private properties = [
    'lvMenus',
    'lvDisabled',
    'lvLabelTemplate',
    'lvSize',
    'lvTrigger',
    'lvTheme',
    'lvPanelClass',
    'lvPosition',
    'lvItemClickClose',
    'lvOuterClosable',
    'lvShow',
    'lvHide',
    'lvItemClick',
    'lvBeforeOpen',
  ];

  constructor(
    private el: ElementRef,
    private renderer: Renderer2,
    private resolver: ComponentFactoryResolver,
    private viewContainer: ViewContainerRef,
  ) {}

  _close(): void {
    if (this.lvTrigger === 'hover') {
      this.dropdown.dropopen$.next(false);
    }
  }

  ngOnChanges(change: SimpleChanges): void {
    if (change.lvMenus || change.lvDisabled) {
      // Update property to component
      this.properties.forEach((property): void => {
        this.updateComponentProperties(property, this[property]);
      });
    }
  }

  ngOnInit(): void {
    const componentRef = this.viewContainer.createComponent(this.ComponentFactory);
    this.dropdown = componentRef.instance;

    // Remove dropdown selector element
    this.renderer.removeChild(this.renderer.parentNode(this.el.nativeElement), componentRef.location.nativeElement);

    // Update property to component
    this.properties.forEach((property): void => {
      this.updateComponentProperties(property, this[property]);
    });
  }

  _open(event: MouseEvent): any {
    const trigger = this.lvTrigger === 'hover' ? 'mouseenter' : this.lvTrigger;
    if (this.lvDisabled || event.type !== trigger) {
      return;
    }

    this.dropdown.mouseevent = event;
    this.dropdown.target = this.el;

    if (trigger === 'mouseenter') {
      this.dropdown.dropopen$.next(true);
    }

    if (trigger === 'click') {
      if (this.dropdown._overlay?.overlayRef.hasAttached()) {
        this.dropdown.dropopen$.next(false);
      } else {
        this.dropdown.dropopen$.next(true);
      }
    }

    if (trigger === 'contextmenu') {
      this.dropdown.openDropdown();
      // eslint-disable-next-line
      return false; // 屏蔽浏览器默认右键菜单
    }
  }

  private updateComponentProperties(key, value): void {
    if (value !== null || value !== undefined) {
      this.dropdown && (this.dropdown[key] = value);
    }
  }
}
